//
// Created by admin on 2024/12/4.
//

#include "CurveMesh.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <QDebug>

CurveMesh::CurveMesh(const CurveSamples &left_curve, const CurveSamples &middle_curve,
                     const CurveSamples &right_curve)
                     : m_left_curve(left_curve), m_middle_curve(middle_curve), m_right_curve(right_curve)
                     {

}

/**
 * @brief 以给定曲线为中心，在两侧添加平行曲线，并三角化这些顶点。
 * @param middle_curve 中心曲线
 * @return -1：给定曲线不足2个采样点 0：网状曲线成功生成。
 */
int CurveMesh::build(const CurveSamples &middle_curve){

    if ( middle_curve.size() < 2) {
        // TODO: LOG ERROR
        return -1;
    }

    m_middle_curve = middle_curve;

    internal_build_curve(m_middle_curve);

    return 0;
}

/**
 * @brief 生成OPENGL可用的顶点数组 （给VBO使用）
 * @return
 */
std::shared_ptr<float> CurveMesh::generate_vertex_buffer()
{
    // 自定义数组删除器
    auto deleter = [](float *p){ delete[] p; };

    int size = m_middle_curve.size() * 3 * 3;
    std::shared_ptr<float> buffer(new float[size], deleter);    // 3条曲线，每条采样点3个坐标

    // 先左边，后中间，最后右边
    int offset = 3*m_middle_curve.size();
    for(int i = 0; i < m_middle_curve.size(); i++) {
        buffer.get()[3*i] = m_left_curve.at(i).x();
        buffer.get()[3*i + 1] = m_left_curve.at(i).y();
        buffer.get()[3*i + 2] = 0.0f;

        buffer.get()[3*i + offset] = m_middle_curve.at(i).x();
        buffer.get()[3*i + 1 + offset] = m_middle_curve.at(i).y();
        buffer.get()[3*i + 2 + offset] = 0.0f;

        buffer.get()[3*i + 2*offset] = m_right_curve.at(i).x();
        buffer.get()[3*i + 1 + 2*offset] = m_right_curve.at(i).y();
        buffer.get()[3*i + 2 + 2*offset] = 0.0f;
    }

    return buffer;
}

/**
 * @brief 生成OPENGL可用的索引数组 （给EBO使用）
 * @param [out] size 索引数组的大小
 * @return
 */
std::shared_ptr<unsigned int> CurveMesh::generate_index_buffer(int &size)
{
    /*
     * |----------    CurveSampleType::LEFT
     * |   /|   /|
     * |  / |  / |
     * | /  | /  |
     * |/   |/   |
     * |----------    CurveSampleType::MIDDLE
     * |   /|   /|
     * |  / |  / |
     * | /  | /  |
     * |/   |/   |
     * |----------    CurveSampleType::RIGHT
     */

    // 自定义数组删除器
    auto deleter = [](unsigned int *p){ delete[] p; };

    size = 3 * (m_middle_curve.size() - 1) * 2 * 2;
    std::shared_ptr<unsigned int> buffer(new unsigned int[size], deleter);  // 左半边三角形数 = 右半边三角形数 = (m_middle_curve.size() - 1) * 2

    // 三角形
    unsigned int cnt = 0;
    for(int i = 0; i < m_middle_curve.size() - 1; i++) {
        buffer.get()[cnt++] = index_at_vertex_buffer(i, CurveSampleType::MIDDLE) / 3;         // 左侧靠边三角形 (除以3之前得到的是这个顶点的x分量在vertex_buffer数组里的下标)
        buffer.get()[cnt++] = index_at_vertex_buffer(i, CurveSampleType::LEFT) / 3;
        buffer.get()[cnt++] = index_at_vertex_buffer(i + 1, CurveSampleType::LEFT) / 3;

        buffer.get()[cnt++] = index_at_vertex_buffer(i + 1, CurveSampleType::MIDDLE) / 3;     // 右侧靠边三角形
        buffer.get()[cnt++] = index_at_vertex_buffer(i, CurveSampleType::RIGHT) / 3;
        buffer.get()[cnt++] = index_at_vertex_buffer(i + 1, CurveSampleType::RIGHT) / 3;

        buffer.get()[cnt++] = index_at_vertex_buffer(i + 1, CurveSampleType::LEFT) / 3;       // 左侧靠中心三角形
        buffer.get()[cnt++] = index_at_vertex_buffer(i, CurveSampleType::MIDDLE) / 3;
        buffer.get()[cnt++] = index_at_vertex_buffer(i + 1, CurveSampleType::MIDDLE) / 3;

        buffer.get()[cnt++] = index_at_vertex_buffer(i, CurveSampleType::RIGHT) / 3;          // 右侧靠中心三角形
        buffer.get()[cnt++] = index_at_vertex_buffer(i, CurveSampleType::MIDDLE) / 3;
        buffer.get()[cnt++] = index_at_vertex_buffer(i + 1, CurveSampleType::MIDDLE) / 3;

    }

    return buffer;
}

/**
 * @brief 为给定的曲线的两侧添加采样点。
 * @param middle_curve 中心曲线
 */
void CurveMesh::internal_build_curve(const CurveSamples &middle_curve){

    int sample_size = middle_curve.size();

    m_left_curve.clear();
    m_right_curve.clear();

    glm::mat4 rotationMatrix = glm::rotate( glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
    glm::vec2 v_last_normal;
    for(int i = 0; i < sample_size - 1; i++) {
        Point p0 = middle_curve.at(i);
        Point p1 = middle_curve.at(i + 1);

        glm::vec3 v_p0_p1( p1.x() - p0.x(), p1.y() - p0.y(), 0.0f);

        glm::vec3 v_normal_3d = glm::normalize( glm::vec3( rotationMatrix * glm::vec4(v_p0_p1, 1.0f)));     // v_p0_p1 旋转90度获得法向量
        glm::vec2 v_normal_2d = WIDTH_SCALE * glm::vec2(v_normal_3d.x, v_normal_3d.y);

        Point p_left_side(p0.x() + v_normal_2d.x, p0.y() + v_normal_2d.y);
        Point p_right_side(p0.x() - v_normal_2d.x, p0.y() - v_normal_2d.y);     // 法向量取反就是另外一边的法向量

        m_left_curve.addSample( p_left_side);
        m_right_curve.addSample( p_right_side);

        if ( i == sample_size -2 ){
            v_last_normal = v_normal_2d;
        }
    }

    // 最后一个点
    Point p_last = middle_curve.at(sample_size - 1);
    Point p_left_side(p_last.x() + v_last_normal.x, p_last.y() + v_last_normal.y);
    m_left_curve.addSample(p_left_side);

    Point p_right_side(p_last.x() - v_last_normal.x, p_last.y() - v_last_normal.y);
    m_right_curve.addSample(p_right_side);
}

/**
 * @brief 获取当前采样点在顶点数组中的索引号。
 * @param sample_index 采样点在CurveSamples中的索引。
 * @param sample_type  采样点所属CurveSamples的类型。
 * @return 顶点数组里的索引号
 */
int CurveMesh::index_at_vertex_buffer(int sample_index, CurveSampleType sample_type)
{
    if (sample_type == CurveSampleType::LEFT) {
        // left curve
        return 3 * sample_index;
    }
    else if( sample_type == CurveSampleType::MIDDLE) {
        // middle curve
        return 3 * m_middle_curve.size() + 3 * sample_index;        // 3 * m_middle_curve.size(): 顶点数组里一个顶点占3个元素，即x,y,z
    }
    else {
        // right curve
        return 2 * 3 * m_middle_curve.size() + 3 * sample_index;
    }
}


